package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.execchain.TunnelRefusedException;
import org.aspectj.weaver.patterns.HasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private VodService vodService;

	@Autowired
	private TrackStatMapper trackStatMapper;

	@Autowired
	private UserFeignClient userFeignClient;

	/**
	 * 保存专辑下声音
	 *
	 * @param trackInfoVo 声音信息VO对象
	 * @return
	 */
	@Override
	public void saveTrackInfo(Long userId, TrackInfoVo trackInfoVo) {

		//1.保存声音
		//1.1 将前端提交声音VO对象转为声音PO对象
		TrackInfo trackInfo = new TrackInfo();
		BeanUtils.copyProperties(trackInfoVo,trackInfo);
		//1.2 为声音对象中基本属性赋值：用户ID、声音来源、声音审核状态（审核通过）
		trackInfo.setUserId(userId);
		trackInfo.setSource("1");
		trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);

		//1.3 根据专辑ID查询专辑信息得到专辑已有声音数量，计算出新增声音序号
		AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());

		trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);

		//1.4 根据文件唯一标识远程调用腾讯点播平台接口获取音频文件信息（音频时长、大小、音频类型）
		TrackMediaInfoVo trackMediaInfoVo = vodService.getTrackMediaInfo(trackInfoVo.getMediaFileId());

		if (trackMediaInfoVo != null){
			trackInfo.setMediaDuration(new BigDecimal(trackMediaInfoVo.getDuration()));
			trackInfo.setMediaSize(trackMediaInfoVo.getSize());
			trackInfo.setMediaType(trackMediaInfoVo.getType());
		}

		//1.5 保存声音得到声音ID
		trackInfoMapper.insert(trackInfo);
		Long trackId = trackInfo.getId();

		// 2.新增声音统计信息
		this.saveTrackStat(trackId,SystemConstant.TRACK_STAT_PLAY,0);
		this.saveTrackStat(trackId,SystemConstant.TRACK_STAT_COMMENT,0);
		this.saveTrackStat(trackId,SystemConstant.TRACK_STAT_PRAISE,0);
		this.saveTrackStat(trackId,SystemConstant.TRACK_STAT_COLLECT,0);

		// 3.更新专辑包含声音数量
		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
		albumInfoMapper.updateById(albumInfo);

	}


	/**
	 * 新增专辑统计信息
	 * @param trackId
	 * @param statType
	 * @param statNum
	 */
	@Override
	public void saveTrackStat(Long trackId, String statType, int statNum) {
		TrackStat trackStat = new TrackStat();
		trackStat.setTrackId(trackId);
		trackStat.setStatNum(statNum);
		trackStat.setStatType(statType);
		trackStatMapper.insert(trackStat);
	}

	/**
	 * 获取当前登录声音分页列表
	 *
	 * @param page           页码
	 * @param limit          页大小
	 * @param trackInfoQuery 查询条件
	 * @return
	 */

	@Override
	public Page<TrackListVo> getUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
		return trackInfoMapper.getUserTackByPage(pageInfo,trackInfoQuery);
	}


	/**
	 * 修改声音
	 *
	 * @param id          声音ID
	 * @param trackInfoVo 声音VO对象
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
		// 1.根据声音 ID 查询声音对象-判断声音是否变更
		TrackInfo trackInfo = trackInfoMapper.selectById(id);

		// 1.2 变更前音频文件唯一标识
		String mediaFileIdBefore = trackInfo.getMediaFileId();

		// 1.3 最新提交音频文件唯一标识
		String mediaFileIdAfter = trackInfoVo.getMediaFileId();

		BeanUtils.copyProperties(trackInfoVo,trackInfo);

		// 2.如果声音变更，获取最新声音相关音频信息(时长、大小、类型) 为 PO 对象赋值
		if (mediaFileIdAfter.equals(mediaFileIdBefore)){
			TrackMediaInfoVo trackMediaInfo = vodService.getTrackMediaInfo(mediaFileIdAfter);
			if (trackMediaInfo != null){
				trackInfo.setMediaType(trackMediaInfo.getType());
				trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfo.getDuration()));
				trackInfo.setMediaSize(trackMediaInfo.getSize());
				// 将变更前音频文件从云点播平台删除
				vodService.deleteTrackMedia(mediaFileIdBefore);
			}
		}
		trackInfoMapper.updateById(trackInfo);
	}

	/**
	 * 删除声音信息
	 * @param id
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteTrackInfoById(Long id) {


		TrackInfo trackInfo  = trackInfoMapper.selectById(id);
		// 删除声音序号

		trackInfoMapper.deleteById(id);

		trackInfoMapper.updateTrackNum(trackInfo.getAlbumId(), trackInfo.getOrderNum());

		// 删除声音统计信息
		trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>()
				.eq(TrackStat::getTrackId,id));


		// 更新专辑包含声音数量
		AlbumInfo albumInfo = albumInfoMapper.selectOne(new LambdaQueryWrapper<AlbumInfo>()
				.eq(AlbumInfo::getId, trackInfo.getAlbumId()));

		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);

		albumInfoMapper.updateById(albumInfo);

		// 删除云点播平台声音文件
		vodService.deleteTrackMedia(trackInfo.getMediaFileId());

	}


	/**
	 * 分页获取专辑下声音列表，动态根据用户情况展示声音付费标识
	 *
	 * @param userId   用户ID
	 * @param albumId  专辑ID
	 * @param pageInfo 分页对象
	 * @return
	 */
	@Override
	public Page<AlbumTrackListVo> getAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long albumId, Long userId) {

		// 1.根据专辑ID分页获取该专辑下包含声音列表（包含声音统计信息）- 默认声音付费标识为 false
		pageInfo = albumInfoMapper.getAlbumTrackPage(pageInfo,albumId);

		// 2. TODO 动态判断当前页中每个声音付费标识，关键点：找出付费情况
		// 2. 根据专辑ID查询专辑信息
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		Assert.notNull(albumInfo,"专辑：{}不存在",albumId);
		String payType = albumInfo.getPayType();

		// 3.处理用户未登录情况
		if (userId == null){
			// 3.1 判断专辑付费 VIP免费（0102）或付费（0103）除了免费试听外声音都应该设置付费标识
			if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)){
				// 3.2 获取本页声音列表，过滤声音需要大于免费试听集数声音付费标识设置为 true
				pageInfo.getRecords().stream().filter(albumTrackListVo -> {
					return albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree();
				}).forEach(albumTrackListVo -> {
					albumTrackListVo.setIsShowPaidMark(true);
				});
			}
		} else {
			// 4. 处理用户已登录情况
			// 4.1 远程调用用户服务获取用户信息得到用户身份
			UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
			Assert.notNull(userInfoVo,"用户：{}不存在",userId);
			Integer isVip = userInfoVo.getIsVip();

			// 4.2 默认设置需要进一步确认购买情况标识：默认false（需要付费）
			Boolean isNeedCheckPayStatus = false;

			// 4.2.1 如果专辑付费类型 vip 免费
			if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)){

				if (isVip.intValue() == 0){
					isNeedCheckPayStatus = true;
				}
				// 当前用户为普通用户或者 vip 已经过期
				if (isVip.intValue() == 1 && new Date().after(userInfoVo.getVipExpireTime())){
					isNeedCheckPayStatus = true;
				}
			}
			// 4.2.2 如果专辑付费类型 为 付费（无论用户身份是否VIP）
			if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)){
				isNeedCheckPayStatus = false;
			}

			// 确认用户是否购买了此专辑或者声音 - 远程调用用户服务中获取当前页面中用户专辑或声音购买情况的方法
			if (isNeedCheckPayStatus) {
				// 获取待检查声音集合
				List<AlbumTrackListVo> needChackTrackList = pageInfo.getRecords().stream().filter(albumTrackListVo ->
						albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()
				).collect(Collectors.toList());


				// 获取 待检查声音 ID集合
				List<Long> needChackTrackIdList = needChackTrackList.stream().map(
						albumTrackListVo -> albumTrackListVo.getTrackId())
						.collect(Collectors.toList());
				// 获取用户已付款声音 ID集合
				Map<Long, Integer> userPayStatusTrackMap = userFeignClient.userIsPaidTrack(userId, albumId, needChackTrackIdList).getData();


				// 匹配 待检查声音ID 和用户已付款ID
				needChackTrackList.stream().forEach(needChackTrack->{
					Integer needPay = userPayStatusTrackMap.get(needChackTrack.getTrackId());
					if (needPay.intValue() == 0){
						needChackTrack.setIsShowPaidMark(true);
					}
				});

			}
		}

		return pageInfo;
	}

	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * MQ监听更新声音统计信息（包含：播放、收藏、点赞、评论）
	 *
	 * @param mqVo
	 */
	@Override
	public void updateTrackStat(TrackStatMqVo mqVo) {
		// 1.做幂等性处理，统一一个消息只处理一次，采用set k（业务消息唯一标识）v NX EX
		// 创建 键值对 getBusinessNo 为随机生成的业务编号标识
		String key = "mq" + mqVo.getBusinessNo();

		// 在redis中设置键值对：如果其中不存在声音统计信息，则创建数据库记录返回true
		Boolean flag = redisTemplate.opsForValue().setIfAbsent(key,mqVo.getBusinessNo(),1, TimeUnit.HOURS);

		if (flag){
			// 更新声音统计信息到mysql数据库
			trackStatMapper.updateStat(mqVo.getTrackId(),mqVo.getStatType(),mqVo.getCount());

			// 更新专辑统计信息（播放量，评论数 + 1，对应专辑 + 1）
			if (SystemConstant.TRACK_STAT_PLAY.equals(mqVo.getStatType())){
				albumInfoMapper.updateStat(mqVo.getAlbumId(),SystemConstant.ALBUM_STAT_PLAY,mqVo.getCount());
			}

			if (SystemConstant.TRACK_STAT_COMMENT.equals(mqVo.getStatType())){
				albumInfoMapper.updateStat(mqVo.getAlbumId(),SystemConstant.ALBUM_STAT_COMMENT,mqVo.getCount());
			}

		}


	}


	/**
	 * 获取声音统计信息
	 * @param trackId
	 * @return
	 */
	@Override
	public TrackStatVo getTrackStatVo(Long trackId) {

		return trackStatMapper.getTrackStatVo(trackId);
	}
}
